home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Source / Unfolder 2.0 Source < prev   
Text File  |  1994-01-10  |  31KB  |  1,297 lines

  1. Date: Wed, 20 Oct 1993 22:53:52 -0400 (EDT)
  2. From: hshubs@BIX.com
  3. Subject: Unfolder 2.0 source
  4.  
  5. This is the source code for Unfolder 2.0, so it might be named unfolder20.c,
  6. and put in the same directory as unfolder20.hqx.
  7.  
  8. /*
  9.  * The Unfolder - program to reconstitute MacBinary (II) encoded files.
  10.  *
  11.  * by various BIXen in the 'mac.hack/tutorial', started on 25 December 1989
  12.  *
  13.  * Originally written by Don Sample, Howard Shubs, and Bob Perkins on
  14.  * the BYTE Information eXchange (BIX), this program is intended to allow
  15.  * people with a C compiler and no way to transfer resources to their Mac
  16.  * to create a way to deal with Mac files which have been downloaded to
  17.  * non-Macintosh hardware.  This program will, when told to Unfold, 
  18.  * reconstitute a MacBinary version 1 or 2 file to its original state.
  19.  *
  20.  * The program will work as-is under THINK C 5.0.2, with just the MacTraps
  21.  * and ANSI-small libraries in the project file with it.  If you are using
  22.  * other environments, you may need to modify the file.  If possible,
  23.  * please use conditional compilation and send a diffs file to Howard Shubs
  24.  * at hshubs@bix.com.
  25.  *
  26.  *
  27.  *  #includes
  28.  */
  29. /*#include "Headers"*/
  30. #include <stdio.h>
  31. #include <stdarg.h>
  32.  
  33. #include <Traps.h>
  34. #include <Errors.h>
  35. #include <Types.h>
  36. #include <GestaltEqu.h>
  37. #include <OSUtils.h>
  38. #include <Memory.h>
  39. #include <Quickdraw.h>
  40. #include <ToolUtils.h>
  41. #include <Fonts.h>
  42. #include <OSEvents.h>
  43. #include <Menus.h>
  44. #include <Events.h>
  45. #include <Windows.h>
  46. #include <Files.h>
  47. #include <Resources.h>
  48. #include <Dialogs.h>
  49. #include <StandardFile.h>
  50. #include <Desk.h>
  51. #include <Finder.h>
  52. #include <Pascal.h>
  53. #include <TextEdit.h>
  54.  
  55.  
  56. /*
  57.  * #defines
  58.  */
  59.  
  60. #define ALL_TYPES            -1L
  61. #define BASE_RES_ID            400
  62. #define NIL_POINTER            0L
  63. #define MOVE_TO_FRONT        -1L
  64. #define    REMOVE_ALL_EVENTS    0
  65. #define MIN_SLEEP            0
  66. #define NIL_MOUSE_REGION    0L
  67. #define SUSPEND_RESUME_BIT    0x0001
  68. #define RESUMING            1
  69.  
  70. #define APPLE_MENU_ID        128
  71. #define FILE_MENU_ID        129
  72. #define EDIT_MENU_ID        130
  73.  
  74. #define ABOUT_ITEM            1
  75. #define UNFOLD_ITEM            1
  76. #define QUIT_ITEM            2
  77.  
  78. #define MACBINARY_VERSION    129
  79. #define SCROLL_WIDTH        15
  80. #define NIL_STRING            "\p"
  81.  
  82. #define NUM_MASTER_BLOCKS    1        /* this number is semi-arbitrary */
  83.  
  84. #define    BF_OWN_APPL            0x0002    /* Some finder info flags not defined */
  85. #define    F_INITED            0x0100    /* in FileMgr.h */
  86. #define    F_CHANGED            0x0200
  87. #define    F_BUSY                0x0400
  88. /*
  89.  *    MacBinary II Header #defines
  90.  */
  91.  
  92. #define OLD_V_N            0
  93. #define LEN_F_N            1
  94. #define FILENAME        2
  95. #define FILE_TYPE        65
  96. #define FILE_CREA        69
  97. #define ORIG_F_FLAGS    73
  98. #define ZERO_FILL_I        74
  99. #define FILE_V_POS        75
  100. #define FILE_H_POS        77
  101. #define WIN_FOL_ID        79
  102. #define PROTECTED        81
  103. #define ZERO_FILL_II    82
  104. #define LEN_D_FORK        83
  105. #define LEN_R_FORK        87
  106. #define F_CREA_DATE        91
  107. #define F_MOD_DATE        95
  108. #define LEN_GET_INFO    99
  109. #define FINDER_FLAGS    101
  110. #define LEN_TOT_F        116
  111. #define LEN_SEC_H        120
  112. #define MBII_VERS_UP    122
  113. #define MBII_VERS_READ    123
  114. #define    CRC                124
  115.  
  116. /*
  117.  *    Structures
  118.  */
  119.  
  120. typedef struct Header                /* MacBinary header data */
  121.     {
  122.     unsigned char    oldVN;            /* old version number */
  123.     char            fName[64];        /* file name */
  124.     FInfo            finderInfo;        /* Original Finder information record */
  125.     unsigned char    protected;
  126.     long            lenDF;            /* length of data fork */
  127.     long            lenRF;            /* length of resource fork */
  128.     long            fCreaDate;        /* file creation date */
  129.     long            fModDate;        /* file's last modified date */
  130.     int                lenGetInfo;        /* length of Get Info comment */
  131.     unsigned char    finderFlags;    /* Low byte of finder flags */
  132.     long            lenTotF;        /* length of total files for unpacking */
  133.     int                lenSecH;        /* length of any secondary header */
  134.     unsigned char    mbIIVersUp;        /* Version of MB II used for upload */
  135.     unsigned char    mbIIVersRead;    /* Version of MB II needed to read file */
  136.     int                crc;
  137.     } Header;
  138.  
  139. typedef struct DialogItem            /* Dialog item */
  140. {
  141.     long            pointer;
  142.     Rect            boundsRect;
  143.     unsigned char    itemType;
  144.     unsigned char    itemData[3];
  145. } DialogItem;
  146.  
  147. typedef struct DialogList            /* DITL record */
  148. {
  149.     short            itemCount;
  150.     DialogItem        items[2];
  151. } DialogList;
  152.  
  153.  
  154. /*
  155.  *    Function Prototypes:
  156.  */
  157.  
  158. void            AboutAppl (void);
  159. /*short int        CalcCRC(unsigned char *dataBuf, short int size);*/
  160. short int        CalcCRC(register unsigned char *dataBuf, register int size);
  161. OSErr            CreateFile(Str255 fName, int vRefNum, OSType creator,
  162.                  OSType type);
  163. Rect             DeviceRect (void);
  164. int                GetAnEvent(int eventMask, EventRecord *event, long sleep,
  165.                  RgnHandle mouseRgn);
  166. void             HandleAppleChoice (short theItem);
  167. void             HandleFileChoice (short theItem);
  168. void             HandleEditChoice (short theItem);
  169. void             HandleEvent (EventRecord *theEvent);
  170. void             HandleMenuChoice (long menuChoice);
  171. void             HandleMouseDown (EventRecord *thisEvent);
  172. unsigned char    HeaderCheck (short int, Header *);
  173. void            Init (void);
  174. void            MessageDialog(char *format, ...);
  175. void            Pause(void);
  176. void             ProcessFile (void);
  177. void            ToolBoxInit(void);
  178. int                TrapAvailable(unsigned trapNumber, int trapType);
  179. void             Unfold (short int refNum, Header *thisHeader,
  180.                  SFReply *outputFile);
  181. int                WNEIsImplemented(void);
  182.  
  183.  
  184. /*
  185.  *  Globals
  186.  *
  187.  */
  188. EventRecord        gTheEvent;
  189. MenuHandle        gAppleMenu;
  190. MenuHandle        gFileMenu;
  191. MenuHandle        gEditMenu;
  192. Point            gWhere;
  193. Boolean            gDone;
  194. Boolean            gWNEExists;
  195. short            gQDVersion;
  196.  
  197.  
  198. /*
  199.  *    FUNCTION:
  200.  *        ToolBoxInit
  201.  *
  202.  *    PURPOSE:
  203.  *        Initialize the ToolBox Managers
  204.  */
  205.  
  206. void    ToolBoxInit()
  207. {
  208.     InitGraf (&(qd.thePort));
  209.     InitFonts ();
  210.     FlushEvents (everyEvent, REMOVE_ALL_EVENTS);
  211.     InitWindows ();
  212.     InitMenus ();
  213.     TEInit ();
  214.     InitDialogs (NIL_POINTER);
  215.     InitCursor ();
  216. }
  217.  
  218. /*
  219.  *    FUNCTION:
  220.  *        Pause
  221.  *
  222.  *    PURPOSE:
  223.  *        Wait for the user to click the mouse, or to press the return
  224.  *        or Enter key.
  225.  */
  226.  
  227. void    Pause()
  228.  
  229. {
  230.     int            button;
  231.     EventRecord    myEvent;
  232.     
  233.     button = false;
  234.     while (!button)
  235.     {
  236.         if (GetAnEvent(everyEvent, &myEvent, 0L, NIL_POINTER))
  237.         {
  238.             if (myEvent.what == mouseDown)
  239.                 button = true;
  240.             else if (myEvent.what == keyDown)
  241.             {
  242.                 if (((myEvent.message & charCodeMask) == 0x0d) ||
  243.                  ((myEvent.message & charCodeMask) == 0x03))
  244.                      button = true;
  245.             }
  246.         }
  247.     }
  248.     while (StillDown())
  249.         ;
  250. }
  251.  
  252. /*
  253.  *    FUNCTION:
  254.  *        MessageDialog
  255.  *
  256.  *    PURPOSE:
  257.  *        Display an error message and then pause until the user clicks the
  258.  *        mouse button
  259.  *
  260.  *    INPUT:
  261.  *        char    *format            - A NULL terminated format string, using 
  262.  *                                  the same rules as printf.
  263.  *        ...                        - Other parameters as specified in the 
  264.  *                                  format string
  265.  */
  266.  
  267. void    MessageDialog(char *format, ...)
  268.  
  269. {
  270.     char            messageStr[256];
  271.     va_list            data;
  272.     OSErr            thisErr;
  273.     
  274.     va_start(data, format);
  275.     vsprintf(messageStr, format, data);
  276.     CtoPstr(messageStr);
  277.     ParamText ((ConstStr255Param) messageStr, NIL_STRING, NIL_STRING, NIL_STRING);
  278.     thisErr = CautionAlert (BASE_RES_ID, NIL_POINTER);
  279.     va_end(data);
  280. }
  281.  
  282. #define _Unimplemented    0xA89F
  283. #define _WaitNextEvent    0xA860
  284.  
  285. /*
  286.  *    FUNCTION:
  287.  *        GetAnEvent
  288.  *
  289.  *    PURPOSE:
  290.  *        Get an event from the input queue using whichever is appropriate:
  291.  *        GetNextEvent, or WaitNextEvent
  292.  *
  293.  *    INPUTS:
  294.  *        int            eventMask        - Types of events to get
  295.  *        EventRecord    *event            - Where to put retreived event
  296.  *        long        sleep            - amount of time to surrender to
  297.  *                                      background tasks
  298.  *        RgnHandle    mouseRgn        - Global region containing mouse.
  299.  *
  300.  *    RETURNS:
  301.  *        int            TRUE if an event was retrieved.
  302.  */
  303.  
  304. enum        /* Which routine to call: Get... or WaitNextEvent            */
  305.     {
  306.     notKnown,
  307.     getNext,
  308.     waitNext
  309.     };
  310.     
  311.  
  312. int        GetAnEvent(int eventMask, EventRecord *event, long sleep,
  313.          RgnHandle mouseRgn)
  314.  
  315. {
  316.     int        result;
  317.     static    int        callRoutine = notKnown;
  318.     
  319.     if (callRoutine == notKnown)
  320.     {
  321.         if (WNEIsImplemented())
  322.             callRoutine = waitNext;
  323.         else
  324.             callRoutine = getNext;
  325.     }
  326.     if (callRoutine == waitNext)
  327.         result = WaitNextEvent(eventMask, event, sleep, mouseRgn);
  328.     else
  329.     {
  330.         SystemTask();
  331.         result = GetNextEvent(eventMask, event);
  332.     }
  333.     return(result);
  334. }
  335.  
  336. /*
  337.  *    FUNCTION:
  338.  *        TrapAvailable
  339.  *
  340.  *    PURPOSE:
  341.  *        Determine if a specific trap routine is available
  342.  *
  343.  *    INPUTS:
  344.  *        unsigned    tNumber        - trap number
  345.  *        int            tType        - trap type (OS or Toolbox)
  346.  *
  347.  *    RETURNS:
  348.  *        int            TRUE if trap is available
  349.  *                    false otherwise
  350.  */
  351.  
  352. TrapAvailable(unsigned tNumber, int tType)
  353.  
  354. {
  355.     /* Check and see if the trap exists. */
  356.     
  357.     return(NGetTrapAddress(tNumber, tType) !=
  358.      GetTrapAddress(_Unimplemented));
  359. }
  360.  
  361. /*
  362.  *    FUNCTION:
  363.  *        WNEIsImplemented
  364.  *
  365.  *    PURPOSE:
  366.  *        Determine if the WaitNextEvent routine is available
  367.  *
  368.  *    RETURNS:
  369.  *        int        TRUE if WNE is available
  370.  *                FALSE otherwise
  371.  */
  372.     
  373. WNEIsImplemented()
  374.  
  375. {
  376.     
  377.     SysEnvRec theWorld; /* used to check if machine has new traps */
  378.     
  379.     /*
  380.      *    Since WaitNextEvent and HFSDispatch both have the same trap
  381.      *    number ($60), we can only call TrapAvailable for WaitNextEvent
  382.      *    if we are on a machine that supports separate OS and Toolbox
  383.      *    trap tables. We call SysEnvirons and check if machineType < 0.
  384.      */
  385.      
  386.     SysEnvirons(1, &theWorld);
  387.     
  388.     /* Even if we got an error from SysEnvirons, the SysEnvirons glue
  389.        has set up machineType. */
  390.     
  391.     if (theWorld.machineType < 0)
  392.     {
  393.         /* this ROM doesn't have separate trap tables or WaitNextEvent */
  394.         return(false);
  395.     }
  396.     else
  397.     {
  398.         /* check for WaitNextEvent */
  399.         return(TrapAvailable(_WaitNextEvent, ToolTrap));
  400.     }
  401. }
  402.  
  403. /*
  404.  *    FUNCTION:
  405.  *        CreateFile
  406.  *
  407.  *    PURPOSE:
  408.  *        Create a file, deleting any previous version if necessary.
  409.  *
  410.  *    INPUTS:
  411.  *        Str255    fName        - File name
  412.  *        int        vRefNum        - Directory to create file in
  413.  *        OSType    creator        - File's creator ID
  414.  *        OSType    type        - File type
  415.  *
  416.  *    RETURNS:
  417.  *        OSErr    - noErr if successful.
  418.  */
  419.  
  420. OSErr    CreateFile(Str255 fName, int vRefNum, OSType creator, OSType type)
  421.  
  422. {
  423.     OSErr    thisErr;
  424.     
  425.     thisErr = Create (fName, vRefNum, creator, type);
  426.     if (thisErr == dupFNErr)
  427.     {
  428.         thisErr = FSDelete (fName, vRefNum);
  429.         if (thisErr == noErr)
  430.             thisErr = Create (fName, vRefNum, creator, type);
  431.     }
  432.     return (thisErr);
  433. }
  434.  
  435. /*
  436.  *    FUNCTION:
  437.  *        CalcCRC
  438.  *
  439.  *    PURPOSE:
  440.  *        Calculate a CCITT CRC for a data buffer
  441.  *
  442.  *    INPUTS:
  443.  *        unsigned char    *data    - pointer to data buffer to perform CRC on
  444.  *        int                size    - size of data buffer
  445.  *
  446.  *    RETURNS:
  447.  *        int                - CRC for data buffer.  If data buffer contains as its
  448.  *                          last 2 bytes the CRC for the previous bytes the return
  449.  *                          value is 0
  450.  */
  451.  
  452. #define CCITT_CRC_GEN    0x1021
  453.  
  454. short int    CalcCRC(register unsigned char *dataBuf, register int size)
  455.  
  456. {
  457.     register unsigned short    crc = 0;
  458.     register unsigned short    dataByte;
  459.     register int    i;
  460.     
  461.     while (size--)
  462.     {
  463.         dataByte = *dataBuf++ << 8;
  464.         for (i = 8; i > 0; i--)
  465.         {
  466.             if ((dataByte ^ crc) & 0x8000)
  467.                 crc = (crc << 1) ^ CCITT_CRC_GEN;
  468.             else
  469.                 crc <<= 1 ;
  470.             dataByte <<= 1;
  471.         }
  472.     }
  473.     return(crc);
  474. }
  475.  
  476.  
  477. /*
  478.  *    FUNCTION:
  479.  *        AboutAppl
  480.  *
  481.  *    PURPOSE:
  482.  *        Tell about the program
  483.  *
  484.  */
  485.  
  486. void AboutAppl (void)
  487. {
  488.     Rect        bounds = {58, 108, 192, 400};
  489.     WindowPtr    thisWindow;
  490.     GrafPtr        oldPort;
  491.  
  492.     GetPort (&oldPort);
  493.     thisWindow = NewWindow (NIL_POINTER, &bounds, "\p", true, 1, (WindowPtr) -1L,
  494.                             false, 0L);
  495.     bounds = thisWindow->portRect;
  496.     SetPort (thisWindow);
  497.     /*          123456789   0   123   4   567890123456789   0123456789    */
  498.     TextBox ("Unfolder\015\015by\015\015Howard S Shubs\015Don Sample\015Bob Perkins", 51L, &bounds, 1);
  499.     Pause ();
  500.     SetPort (oldPort);
  501.     DisposeWindow (thisWindow);
  502. }
  503.  
  504.  
  505. /*
  506.  *    FUNCTION:
  507.  *        DeviceRect
  508.  *
  509.  *    PURPOSE:
  510.  *        Get the bounds rect of the main screen.
  511.  *
  512.  *    RETURNS:
  513.  *        Rect    the bounds rect of the main screen.
  514.  *
  515.  */
  516.  
  517. Rect DeviceRect (void)
  518. {
  519.     GDHandle        device;
  520.     Rect            devRect;
  521.  
  522.     if (gQDVersion > 0)
  523.     {
  524.         device = GetMainDevice ();
  525.         devRect = (**(device)).gdRect;
  526.     }
  527.     else
  528.         devRect = qd.screenBits.bounds;
  529.     
  530.     return devRect;
  531. }
  532.  
  533.  
  534. /*
  535.  *    FUNCTION:
  536.  *        HandleAppleChoice
  537.  *
  538.  *    PURPOSE:
  539.  *        Deal with selection from the Apple menu
  540.  *
  541.  *    INPUTS:
  542.  *        short int    theItem: contains the selection from the menu
  543.  *
  544.  */
  545.  
  546. void HandleAppleChoice (short theItem)
  547. {
  548.     Str255        accName;
  549.     int            accNumber;
  550.     short int    itemNumber;
  551.     DialogPtr    AboutDialog;
  552.     
  553.     switch (theItem)
  554.     {
  555.         case ABOUT_ITEM:
  556.             AboutAppl ();
  557.             break;
  558.         default:
  559.             GetItem (gAppleMenu, theItem, accName);
  560.             accNumber = OpenDeskAcc (accName);
  561.             break;        
  562.     }
  563. }
  564.  
  565. /*
  566.  *    FUNCTION:
  567.  *        HandleEditChoice
  568.  *
  569.  *    PURPOSE:
  570.  *        Deal with selection from the Edit menu by passing them to the
  571.  *        system.
  572.  *
  573.  *    INPUTS:
  574.  *        short int    theItem: contains the selection from the menu
  575.  *
  576.  */
  577.  
  578. void HandleEditChoice (short theItem)
  579. {
  580.     if (!SystemEdit (theItem - 1))
  581.         ;
  582. }
  583.  
  584.  
  585. /*
  586.  *    FUNCTION:
  587.  *        HandleFileChoice
  588.  *
  589.  *    PURPOSE:
  590.  *        Deal with selection from the File menu
  591.  *
  592.  *    INPUTS:
  593.  *        short int    theItem: contains the selection from the menu
  594.  *
  595.  */
  596. /*{}{}{}*/
  597. void HandleEvent (EventRecord *thisEvent)
  598. {
  599.     char    theChar;
  600.  
  601.     if (gWNEExists)
  602.         WaitNextEvent (everyEvent, thisEvent, MIN_SLEEP, NIL_MOUSE_REGION);
  603.     else
  604.     {
  605.         SystemTask ();
  606.         GetNextEvent (everyEvent, thisEvent);
  607.     }
  608.     
  609.     switch (thisEvent->what)
  610.     {
  611.         case nullEvent:
  612.             break;
  613.         case mouseDown:
  614.             HandleMouseDown (thisEvent);
  615.             break;
  616.         case keyDown:
  617.         case autoKey:
  618.             theChar = (thisEvent->message) & charCodeMask;
  619.             if (( (thisEvent->modifiers) & cmdKey) != 0)
  620.                 HandleMenuChoice (MenuKey(theChar));
  621.             break;
  622.         case updateEvt:
  623. /*            BeginUpdate ((WindowPtr) (thisEvent->message) );
  624.             UpdateWindow ((WindowPtr) (thisEvent->message));
  625.             EndUpdate ((WindowPtr) (thisEvent->message) );*/
  626.             break;
  627.         case app4Evt:
  628.             if ( ( (thisEvent->message) & SUSPEND_RESUME_BIT) == RESUMING)
  629.             {
  630. /*                    gState.applFlags.isSuspended =
  631.                                     ((thisEvent->message) & 0x01) == 0;*/
  632.             }
  633.             break;
  634.     }
  635. }
  636.  
  637. /*
  638.  *    FUNCTION:
  639.  *        HandleFileChoice
  640.  *
  641.  *    PURPOSE:
  642.  *        Deal with selection from the File menu
  643.  *
  644.  *    INPUTS:
  645.  *        short int    theItem: contains the selection from the menu
  646.  *
  647.  */
  648.  
  649. void HandleFileChoice (short theItem)
  650. {
  651.     switch (theItem)
  652.     {
  653.         case UNFOLD_ITEM:
  654.             ProcessFile ();
  655.             break;
  656.         case QUIT_ITEM:
  657.             gDone = true;
  658.             break;
  659.     }
  660. }
  661.  
  662. /*
  663.  *    FUNCTION:
  664.  *        HandleMenuChoice
  665.  *
  666.  *    PURPOSE:
  667.  *        Deal with selection of menu items
  668.  *
  669.  *    INPUTS:
  670.  *        long int    menuChoice: contains the selection from a menu
  671.  *
  672.  *    RETURNS:
  673.  *        unsigned char                - TRUE if the file is MacBinary
  674.  *        
  675.  */
  676.  
  677. void HandleMenuChoice (long menuChoice)
  678. {
  679.     short    theMenu;
  680.     short    theItem;
  681.     
  682.     if (menuChoice != 0)
  683.     {
  684.         theMenu = HiWord (menuChoice);
  685.         theItem = LoWord (menuChoice);
  686.         switch (theMenu)
  687.         {
  688.             case APPLE_MENU_ID:
  689.                 HandleAppleChoice (theItem);
  690.                 break;
  691.             case FILE_MENU_ID:
  692.                 HandleFileChoice (theItem);
  693.                 break;
  694.             case EDIT_MENU_ID:
  695.                 HandleEditChoice (theItem);
  696.                 break;
  697.         }
  698.         HiliteMenu (0);
  699.     }
  700. }
  701.  
  702. /*
  703.  *    FUNCTION:
  704.  *        HandleMouseDown
  705.  *
  706.  *    PURPOSE:
  707.  *        Deal with mouseDown events.
  708.  *
  709.  *    INPUTS:
  710.  *        EventRecord    *thisEvent   points to the current event record.
  711.  *
  712.  */
  713.  
  714. void HandleMouseDown (EventRecord *thisEvent)
  715. {
  716.     Rect            devRect;
  717.     long            menuChoice;
  718.     short int        thePart;
  719.     WindowPeek        window;
  720.     long            windSize;
  721.     
  722.     thePart = FindWindow ((thisEvent->where), (WindowPtr *) &window);
  723.     switch (thePart)
  724.     {
  725.         case inMenuBar:
  726.             menuChoice = MenuSelect ((thisEvent->where));
  727.             HandleMenuChoice (menuChoice);
  728.             break;
  729.         case inSysWindow:
  730.             SystemClick (thisEvent, (WindowPtr) window);
  731.             break;
  732.         case inDrag:
  733.             devRect = DeviceRect ();
  734.             DragWindow ((WindowPtr) window, (thisEvent->where), &devRect);
  735.             break;
  736.         case inGrow:
  737.             devRect = DeviceRect ();
  738.             windSize = GrowWindow ((WindowPtr) window, (thisEvent->where),
  739.                                  &devRect);
  740.             if (windSize > 0)
  741.             {
  742.                 Rect    bad;
  743.                 Point    p;
  744.             
  745.                 /* invalidate the part of the window with the grow icon */    
  746.                 bad = (window->port).portRect;
  747.                 bad.top = bad.bottom - SCROLL_WIDTH;
  748.                 bad.left = bad.right - SCROLL_WIDTH;
  749.                 InvalRect (&bad);
  750.             }
  751.             SizeWindow ((WindowPtr) window, LoWord (windSize),
  752.                         HiWord (windSize), true);
  753.             break;
  754.         case inGoAway:
  755.             /*if (TrackGoAway ((WindowPtr) window, (thisEvent->where) ) )
  756.                 DoCloseWindow ((WindowPtr) window);*/
  757.             break;
  758.     }
  759. }
  760.  
  761. /*
  762.  *    FUNCTION:
  763.  *        HeaderCheck
  764.  *
  765.  *    PURPOSE:
  766.  *        Look at a selected file to determine if it is a MacBinary (II)
  767.  *        file or not.
  768.  *
  769.  *    INPUTS:
  770.  *        short int        refNum        - reference number of the opened file to be
  771.  *                                       inspected.
  772.  *        Header            *thisHeader    - MacBinary header record to return data in.
  773.  *
  774.  *    RETURNS:
  775.  *        unsigned char                - TRUE if the file is MacBinary
  776.  *        
  777.  */
  778.  
  779. unsigned char HeaderCheck (short int refNum, Header *thisHeader)
  780. {
  781.     long int        count = 128;
  782.     OSErr            thisErr;
  783.     short int        i;
  784.     unsigned char    *buffer;
  785.     unsigned char    soFarSoGood = false;
  786.  
  787.     /* Go to the beginning of the file pointed to by refNum and
  788.        read the first (count) bytes. */
  789.     thisErr = SetFPos (refNum, fsFromStart, 0L);
  790.     if (thisErr == noErr)
  791.     {
  792.         buffer = (unsigned char *) NewPtr (count);
  793.         if ((thisErr = MemError()) == noErr)
  794.         {
  795.             thisErr = FSRead (refNum, &count, buffer);
  796.             if (thisErr == noErr)
  797.             {
  798.                 /* Transfer the data read from the file into thisHeader */
  799.                 /* First, check to make sure that the mbz bytes ARE zero */
  800.                 if (buffer[OLD_V_N] == 0 && buffer[ZERO_FILL_I] == 0)
  801.                     soFarSoGood = true;
  802.                 
  803.                 /* Transfer the file name, and make sure its length is > 0 */
  804.                 if (buffer[LEN_F_N] > 0 && buffer[LEN_F_N] < 64 && soFarSoGood)
  805.                 {
  806.                     BlockMove (&buffer[LEN_F_N], &(thisHeader->fName),
  807.                                 buffer[LEN_F_N]+1);
  808.                     
  809.                     /* Get the finderInfo */
  810.                     BlockMove (&buffer[FILE_TYPE], &(thisHeader->finderInfo),
  811.                                 sizeof(FInfo));
  812.                                         
  813.                     /* Get the "protected" flag */
  814.                     thisHeader->protected = buffer[PROTECTED];
  815.                     
  816.                     /* Get the fork sizes */
  817.                     BlockMove (&buffer[LEN_D_FORK], &(thisHeader->lenDF),
  818.                                 8);
  819.                     
  820.                     /* Get file's dates */
  821.                     BlockMove (&buffer[F_CREA_DATE], &(thisHeader->fCreaDate),
  822.                                 8);
  823.  
  824.                     /* Get length of "Get Info" comment */
  825.                     BlockMove (&buffer[LEN_GET_INFO], &(thisHeader->lenGetInfo),
  826.                                 2);
  827.                     
  828.                     /* Low Finder flags */
  829.                     thisHeader->finderFlags = buffer[FINDER_FLAGS];
  830.                     
  831.                     /* Total size of files */
  832.                     BlockMove (&buffer[LEN_TOT_F], &(thisHeader->lenTotF),
  833.                                 4);
  834.                     
  835.                     /* Get length of secondary header */
  836.                     BlockMove (&buffer[LEN_SEC_H], &(thisHeader->lenSecH),
  837.                                 2);
  838.                     
  839.                     /* Get the versions of MacBinary II */
  840.                     thisHeader->mbIIVersUp = buffer[MBII_VERS_UP];
  841.                     thisHeader->mbIIVersRead = buffer[MBII_VERS_READ];
  842.                     
  843.                     /* Get the CRC of the header */
  844.                     BlockMove (&buffer[CRC], &(thisHeader->crc), 2);
  845.                     
  846.                     soFarSoGood = (CalcCRC(buffer, 124)
  847.                                             == thisHeader->crc)
  848.                                 && (buffer[MBII_VERS_READ] == MACBINARY_VERSION);
  849.  
  850.                     if (!soFarSoGood && (buffer[ZERO_FILL_II] == 0))
  851.                     {
  852.                         /* Make more comparisons to make sure that this
  853.                            is really a MacBinary I file. */
  854.                         for (i = 101; buffer[i]==0 && i < 126; i++);
  855.                         
  856.                         /* If "i" is >= 126, the loop above completed
  857.                            without finding a non-zero, so this is MacBinary I.  */
  858.                         if (soFarSoGood = i >= 126)
  859.                         {
  860.                             /* It is, it really _is_ MacBinary I */
  861.                             
  862.                             /* Low Finder flags */
  863.                             thisHeader->finderFlags = 0;
  864.                     
  865.                             /* Total size of files */
  866.                             thisHeader->lenTotF = 0;
  867.                     
  868.                             /* Get length of secondary header */
  869.                             thisHeader->lenSecH = 0;
  870.                     
  871.                             /* Get the versions of MacBinary II */
  872.                             thisHeader->mbIIVersUp = MACBINARY_VERSION;
  873.                             thisHeader->mbIIVersRead = MACBINARY_VERSION;
  874.                     
  875.                             /* Get the CRC of the header */
  876.                             thisHeader->crc = 0;
  877.                         }
  878.                     }
  879.                     
  880.                 }
  881.                 else
  882.                     soFarSoGood = false;
  883.             };
  884.             DisposPtr ((Ptr) buffer);
  885.             thisErr = MemError ();
  886.         }
  887.     }
  888.     if (thisErr != noErr)
  889.         MessageDialog("Error: %d reading MacBinary header", thisErr);
  890.     else if (!soFarSoGood)
  891.         MessageDialog("This is not a MacBinary file");
  892.     return soFarSoGood;
  893. }
  894.  
  895.  
  896. /*
  897.  *    FUNCTION:
  898.  *        Unfold
  899.  *
  900.  *    PURPOSE:
  901.  *        Restore the forks of a MacBinary encoded file.
  902.  *
  903.  *    INPUTS:
  904.  *        short int        refNum        - reference number of the opened file to be
  905.  *                                       used as input.
  906.  *        Header            *thisHeader - MacBinary header information
  907.  *        SFReply            *thisFile    - Information needed to access the created
  908.  *                                      file.
  909.  *
  910.  *    RETURNS:
  911.  *        unsigned char                - TRUE if the file is MacBinary
  912.  *        Header            *thisHeader    - MacBinary header record to return data in.
  913.  *        
  914.  */
  915.  
  916. void Unfold (short int refNum, Header *thisHeader, SFReply *thisFile)
  917. {
  918.     char        *buffer;
  919.     char        bufferAllocated;
  920.     long int    buffSize;
  921.     long int    forkSize;
  922.     long int    offSet;
  923. /*    long int    grow;*/
  924.     short int    outRefNum;        /* reference number for the output file */
  925.     OSErr        thisErr;        /* status code */
  926.     
  927.     /* Allocate memory for a buffer by this rule:
  928.         Never allocate less than MIN_BUFFER, or more than MAX_BUFFER.
  929.         Between those two, allocate what's available less LEAVE_FREE,
  930.         as long as that isn't less than MIN_BUFFER. */
  931. #define MIN_BUFFER    4*1024L
  932. #define MAX_BUFFER    256*1024L
  933. #define LEAVE_FREE    32*1024L
  934.     buffSize = MaxBlock ();  /*MaxMem (&grow);*/
  935.     buffSize = buffSize > MAX_BUFFER + LEAVE_FREE ?
  936.                 MAX_BUFFER :
  937.                 buffSize < MIN_BUFFER ?
  938.                     MIN_BUFFER :
  939.                     buffSize - LEAVE_FREE < MIN_BUFFER ?
  940.                         MIN_BUFFER :
  941.                         buffSize - LEAVE_FREE;
  942.  
  943.     buffer = NewPtr (buffSize);
  944.     bufferAllocated = (thisErr = MemError()) == noErr;
  945.     
  946.     if (!bufferAllocated)
  947.         MessageDialog ("Couldn't allocate a work buffer.  Give this program more memory, then run it again.");
  948.     
  949.     /* Is there a data fork?  If there is, rebuild it. */
  950.     if ((thisHeader->lenDF > 0) && bufferAllocated)
  951.     {
  952.         Ptr        source;
  953.         Size    count;
  954.         Str255    dest;
  955.     
  956.         source = (Ptr) &((*thisFile).fName);
  957.         count = (char) *source;
  958.         count = BitAnd (count, 0x000000ffL) + 1L;
  959.         BlockMove (source, dest, count);    /* {}{}{} */
  960.     
  961.         forkSize = thisHeader->lenDF;
  962.         if ((thisErr = FSOpen (dest, thisFile->vRefNum, &outRefNum))
  963.          == noErr)
  964.         {
  965.             /* position beyond the MacBinary header */
  966.             if ((thisErr = SetFPos (refNum, fsFromStart, 128)) == noErr)
  967.             {
  968.                 while (thisErr == noErr && forkSize > buffSize)
  969.                 {
  970.                     thisErr = FSRead (refNum, &buffSize, buffer);
  971.                     if (thisErr == noErr)
  972.                         thisErr = FSWrite (outRefNum, &buffSize, buffer);
  973.                     forkSize -= buffSize;
  974.                 }
  975.                 if (thisErr == noErr)
  976.                 {
  977.                     thisErr = FSRead(refNum, &forkSize, buffer);
  978.                     if (thisErr == noErr)
  979.                         thisErr = FSWrite (outRefNum, &forkSize, buffer);
  980.                 }
  981.             }
  982.             if (thisErr == noErr)
  983.             {
  984.                 thisErr = FSClose (outRefNum);
  985.                 if (thisErr == noErr)
  986.                     thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
  987.                 else
  988.                     FlushVol (NIL_POINTER, thisFile->vRefNum);
  989.             }
  990.             else
  991.             {
  992.                 FSClose (outRefNum);
  993.                 FlushVol (NIL_POINTER, thisFile->vRefNum);
  994.             }
  995.         }
  996.     }
  997.     
  998.     /* Is there a resource fork?  If there is, rebuild it. */
  999.     if ((thisHeader->lenRF > 0) && bufferAllocated)
  1000.     {
  1001.         Ptr        source;
  1002.         Size    count;
  1003.         Str255    dest;
  1004.     
  1005.         source = (Ptr) &((*thisFile).fName);
  1006.         count = (char) *source;
  1007.         count = BitAnd (count, 0x000000ffL) + 1L;
  1008.         BlockMove (source, dest, count);    /* {}{}{} */
  1009.     
  1010.         forkSize = thisHeader->lenRF;
  1011.         if ((thisErr = OpenRF (dest, thisFile->vRefNum, &outRefNum))
  1012.          == noErr)
  1013.         {
  1014.             /* position beyond the MacBinary header and the Data Fork */
  1015.             offSet = (128 - (thisHeader->lenDF % 128)) & 0x7f;
  1016.             if ((thisErr = SetFPos (refNum, fsFromStart,
  1017.              (thisHeader->lenDF) + offSet + 128)) == noErr)
  1018.             {
  1019.                 while (thisErr == noErr && forkSize > buffSize)
  1020.                 {
  1021.                     thisErr = FSRead (refNum, &buffSize, buffer);
  1022.                     if (thisErr == noErr)
  1023.                         thisErr = FSWrite (outRefNum, &buffSize, buffer);
  1024.                     forkSize -= buffSize;
  1025.                 }
  1026.                 if (thisErr == noErr)
  1027.                 {
  1028.                     thisErr = FSRead(refNum, &forkSize, buffer);
  1029.                     if (thisErr == noErr)
  1030.                         thisErr = FSWrite (outRefNum, &forkSize, buffer);
  1031.                 }
  1032.             }
  1033.             if (thisErr == noErr)
  1034.             {
  1035.                 thisErr = FSClose (outRefNum);
  1036.                 if (thisErr == noErr)
  1037.                     thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
  1038.                 else
  1039.                     FlushVol (NIL_POINTER, thisFile->vRefNum);
  1040.             }
  1041.             else
  1042.             {
  1043.                 FSClose (outRefNum);
  1044.                 FlushVol (NIL_POINTER, thisFile->vRefNum);
  1045.             }
  1046.         }
  1047.     }
  1048.     if (bufferAllocated)
  1049.         DisposPtr (buffer);
  1050.     if (thisErr != noErr)
  1051.         MessageDialog("Error: %d unfolding MacBinary file", thisErr);
  1052. }
  1053.  
  1054.  
  1055. /*
  1056.  *    FUNCTION:
  1057.  *        ProcessFile
  1058.  *
  1059.  *    PURPOSE:
  1060.  *        Determine our input and output files, then do the processing.
  1061.  *
  1062.  */
  1063.  
  1064. void ProcessFile (void)
  1065. {
  1066.     Boolean            running;
  1067.     int                i;
  1068.     SFTypeList        typeList;
  1069.     SFReply            inputFile, outputFile;
  1070.     Header            mbHeader;
  1071.     OSErr            thisErr;
  1072.     short int        refNum;
  1073.     ParamBlockRec    paramBlock;
  1074.     unsigned int    finderFlags;
  1075.     
  1076.     SFGetFile (gWhere, NIL_POINTER, NIL_POINTER, ALL_TYPES, typeList,
  1077.             NIL_POINTER, &inputFile);
  1078.     running = inputFile.good == true;
  1079.          
  1080.     if (running)
  1081.     {
  1082.         if ((thisErr = FSOpen (inputFile.fName, inputFile.vRefNum,
  1083.          &refNum)) != noErr)
  1084.         {
  1085.             PtoCstr((unsigned char *) inputFile.fName);
  1086.             MessageDialog("Error: %d opening file %s", thisErr, inputFile.fName);
  1087.         }
  1088.  
  1089.         /* read the MacBinary header.  If it's okay, decode the file. */
  1090.         else if (HeaderCheck (refNum, &mbHeader))
  1091.         {
  1092.             Str255    *string = (Str255 *) &(mbHeader.fName);
  1093.             
  1094.             SFPutFile (gWhere, NIL_POINTER, *string,
  1095.                     NIL_POINTER, &outputFile);
  1096.             if (outputFile.good)
  1097.             {
  1098.                    if ((thisErr = CreateFile(outputFile.fName,
  1099.                     outputFile.vRefNum, mbHeader.finderInfo.fdCreator,
  1100.                  mbHeader.finderInfo.fdType)) != noErr)
  1101.                 {
  1102.                      PtoCstr((unsigned char *) outputFile.fName);
  1103.                      MessageDialog("Error: %d creating file %s", thisErr,
  1104.                       outputFile.fName);
  1105.                     running = false;
  1106.                 }
  1107.                 else
  1108.                 {
  1109.                     /* copy data & resource forks from input file */
  1110.                     Unfold (refNum, &mbHeader, &outputFile);
  1111.                     
  1112.                     paramBlock.fileParam.ioCompletion = NIL_POINTER;
  1113.                     paramBlock.fileParam.ioNamePtr = (StringPtr) &outputFile.fName;
  1114.                     paramBlock.fileParam.ioVRefNum = outputFile.vRefNum;
  1115.                     paramBlock.fileParam.ioFVersNum = 0;
  1116.                     /* Set the finder Info for the file */
  1117.                     paramBlock.fileParam.ioFlFndrInfo = mbHeader.finderInfo;
  1118.                     finderFlags = mbHeader.finderInfo.fdFlags;
  1119.                     finderFlags |= mbHeader.finderFlags;
  1120.                     finderFlags &= ~(fOnDesk | BF_OWN_APPL | F_INITED | 
  1121.                      F_CHANGED | F_BUSY);
  1122.                     paramBlock.fileParam.ioFlFndrInfo.fdFlags = finderFlags;
  1123.                     paramBlock.fileParam.ioFlFndrInfo.fdFldr = 0;
  1124.                     paramBlock.fileParam.ioFlFndrInfo.fdLocation.h = 0;
  1125.                     paramBlock.fileParam.ioFlFndrInfo.fdLocation.v = 0;
  1126.                     /* Set the Creation and Modification dates on the file */
  1127.                     paramBlock.fileParam.ioFlCrDat = mbHeader.fCreaDate;
  1128.                     paramBlock.fileParam.ioFlMdDat = mbHeader.fModDate;
  1129.                     thisErr = PBSetFInfo (¶mBlock, false);
  1130.                     if (thisErr != noErr)
  1131.                         MessageDialog("Error: %d, setting file attributes", thisErr);
  1132.                 }
  1133.             }
  1134.         }
  1135.         thisErr = FSClose (refNum);
  1136.     }
  1137. }
  1138.  
  1139.  
  1140. /*
  1141.  *    FUNCTION:
  1142.  *        Init
  1143.  *
  1144.  *    PURPOSE:
  1145.  *        Initialize the environment, establish menus, and set variables.
  1146.  *
  1147.  *  SIDE EFFECTS:
  1148.  *        Initializes the environment, establishes menus, and initializes
  1149.  *        the global variables.
  1150.  *        
  1151.  */
  1152.  
  1153. void Init (void)
  1154. {
  1155.     long            response;
  1156.     Boolean            gestaltExists;
  1157.     Boolean            sysEnvironsExists;
  1158.     SysEnvRec        *theWorld;
  1159.     OSErr            thisErr;
  1160.     Handle            thisHandle;
  1161.     DialogList        errorDITL;
  1162.     AlertTemplate    thisAlert;
  1163.  
  1164.  
  1165.     /* Initialize everything                           */
  1166.     /* Init the toolbox routines */
  1167.     ToolBoxInit();
  1168.     
  1169.     /* Make menus */
  1170.     gAppleMenu = NewMenu (APPLE_MENU_ID, "\p\024");
  1171.     gFileMenu = NewMenu (FILE_MENU_ID, "\pFile");
  1172.     gEditMenu = NewMenu (EDIT_MENU_ID, "\pEdit");
  1173.     InsertMenu (gAppleMenu, 0);
  1174.     InsertMenu (gFileMenu, 0);
  1175.     InsertMenu (gEditMenu, 0);
  1176.     
  1177.     /* Add items to menus */
  1178.     AppendMenu (gAppleMenu, "\pAbout Unfolder...;(-");
  1179.     AddResMenu (gAppleMenu, 'DRVR');
  1180.     AppendMenu (gFileMenu, "\pUnfold.../U;Quit/Q");
  1181.     AppendMenu (gEditMenu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/X;Clear");
  1182.  
  1183.     /* Draw the menu bar */
  1184.     DrawMenuBar ();
  1185.         
  1186.     /* Specify the point at which the SF dialogs will appear */
  1187.     gWhere.v = 100;
  1188.     gWhere.h = 100;
  1189.  
  1190.     /* We're not done yet */
  1191.     gDone = false;
  1192.     
  1193.     /* Can we call WaitNextEvent? */
  1194.     gWNEExists = WNEIsImplemented();
  1195.     
  1196.     /* What kind of Quickdraw do we have? */
  1197.     gestaltExists = TrapAvailable (_GestaltDispatch, ToolTrap);
  1198.     sysEnvironsExists = TrapAvailable (_SysEnvirons, OSTrap);
  1199.     if (gestaltExists)
  1200.         thisErr = Gestalt ('qd  ', &response);
  1201.     else 
  1202.     {
  1203.         if (sysEnvironsExists)
  1204.         {
  1205.             theWorld = (SysEnvRec *) NewPtr (sizeof (SysEnvRec));
  1206.             if (theWorld == NIL_POINTER)
  1207.             {
  1208.                 thisErr = MemError ();
  1209.                 MessageDialog("Error: %d allocating memory", thisErr);
  1210.             }
  1211.             thisErr = SysEnvirons (1, theWorld);
  1212.             response = (long) theWorld->hasColorQD;
  1213.             DisposPtr ((Ptr) theWorld);
  1214.         }
  1215.         else
  1216.             response = 0L;
  1217.     }
  1218.     gQDVersion = LoWord (response) >> 8;
  1219.     
  1220.     
  1221.     /* Do the miscellaneous resources exist yet?  Check for one.  If
  1222.        that doesn't exist, assume that none of them do and create them. */
  1223.     thisHandle = GetResource ('ALRT', BASE_RES_ID);
  1224.     if (thisHandle == NIL_POINTER)
  1225.     {
  1226.         /* Construct the error DITL    see IM I-427 */
  1227.         errorDITL.itemCount = 1;        /* total of two items */
  1228.     
  1229.         /* OK button */
  1230.         errorDITL.items[0].pointer = 0L;
  1231.         errorDITL.items[0].boundsRect.top = 96;
  1232.         errorDITL.items[0].boundsRect.left = 137;
  1233.         errorDITL.items[0].boundsRect.bottom = 116;
  1234.         errorDITL.items[0].boundsRect.right = 195;
  1235.         errorDITL.items[0].itemType = (char) btnCtrl | ctrlItem;
  1236.         errorDITL.items[0].itemData[0] = 2;
  1237.         errorDITL.items[0].itemData[1] = 'O';
  1238.         errorDITL.items[0].itemData[2] = 'K';
  1239.     
  1240.         /* Static Text */
  1241.         errorDITL.items[1].pointer = 0L;
  1242.         errorDITL.items[1].boundsRect.top = 13;
  1243.         errorDITL.items[1].boundsRect.left = 65;
  1244.         errorDITL.items[1].boundsRect.bottom = 82;
  1245.         errorDITL.items[1].boundsRect.right = 318;
  1246.         errorDITL.items[1].itemType = (char) statText;
  1247.         errorDITL.items[1].itemData[0] = 2;
  1248.         errorDITL.items[1].itemData[1] = '^';
  1249.         errorDITL.items[1].itemData[2] = '0';
  1250.     
  1251.         /* Make the DITL into a resource */
  1252.         thisHandle = NewHandle (sizeof (DialogList));
  1253.         BlockMove (&errorDITL, *thisHandle, sizeof (DialogList));
  1254.         AddResource (thisHandle, 'DITL', BASE_RES_ID, "\pError DITL");
  1255.         thisErr = ResError ();
  1256.     
  1257.     
  1258.         /* Construct the error ALRT   IM-I 426 */
  1259.         thisAlert.boundsRect.top = 40;
  1260.         thisAlert.boundsRect.left = 40;
  1261.         thisAlert.boundsRect.bottom = 172;
  1262.         thisAlert.boundsRect.right = 368;
  1263.         thisAlert.itemsID = BASE_RES_ID;
  1264.         thisAlert.stages = 0x7775;
  1265.     
  1266.         /* Make the ALRT into a resource */
  1267.         thisHandle = NewHandle (sizeof (AlertTemplate));
  1268.         BlockMove (&thisAlert, *thisHandle, sizeof (AlertTemplate));
  1269.         AddResource (thisHandle, 'ALRT', BASE_RES_ID, "\pError ALRT");
  1270.         thisErr = ResError ();
  1271.     }
  1272.     else
  1273.         ReleaseResource (thisHandle);
  1274. }
  1275.  
  1276.  
  1277. main()
  1278. {
  1279.     int                i;
  1280.  
  1281.     /* Maximize available memory */
  1282.     MaxApplZone ();
  1283.  
  1284.     /* Be sure to have plenty of master pointer blocks */
  1285.     for (i = 0; i < NUM_MASTER_BLOCKS; i++)
  1286.         MoreMasters();
  1287.     
  1288.     Init ();
  1289.  
  1290.     while (!gDone)
  1291.     {
  1292.         HandleEvent (&gTheEvent);
  1293.     }
  1294.     
  1295. } /* main */
  1296.  
  1297.